package com.ibm.risk.irmp.flowable.listener;

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.flowable.common.engine.api.delegate.Expression;
import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.delegate.ExecutionListener;
import org.flowable.engine.impl.el.FixedValue;
import org.flowable.task.service.delegate.DelegateTask;
import org.flowable.task.service.delegate.TaskListener;
import org.flowable.variable.api.delegate.VariableScope;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;

import java.util.Map;

@Data
@Slf4j
public class RestCallEventListener implements TaskListener, ExecutionListener {
    private Expression url;
    private Expression method;
    private Expression body;
    private Expression headers;
    private Expression resultVariable;
    private Expression resultVarType;
    private Expression condition;

    @Override
    public void notify(DelegateTask task) {
        restCall(task);
    }

    private void restCall(VariableScope task) {
        if (condition != null && Boolean.TRUE != condition.getValue(task)) {
            log.info("condition is false, skipped rest call: {}", url.getExpressionText());
            return;
        }
        log.info("execute rest event, url:{}, method:{}, body:{}", url.getExpressionText(), method.getExpressionText(), body);
        RestTemplate client = new RestTemplate();
        HttpHeaders header = new HttpHeaders();
        ObjectMapper om = new ObjectMapper();
        Object bodyObj = null;
        try {
            if (headers != null) {
                Map<String, String> map = om.readValue((String) headers.getValue(task), Map.class);
                map.forEach((k, v) -> header.add(k, v));

            }
            if (method == null) {
                method = new FixedValue("GET");
            }
            if (body != null) {
                String bodyValue = (String) body.getValue(task);
                log.info("body:{}", bodyValue);
                bodyObj = om.readValue(bodyValue, Map.class);
            }
            String reqUrl = url.getValue(task).toString();

            Class<?> responseClass = null;
            if (resultVarType != null) {
                responseClass = (Class.forName(resultVarType.getExpressionText()));
                log.info("request of url {}, result:{}", reqUrl, responseClass);
            }

            ResponseEntity<?> resp = client.exchange(reqUrl,
                    HttpMethod.valueOf(method.getExpressionText().toUpperCase()),
                    new HttpEntity<>(header),
                    responseClass,
                    bodyObj);
            if (resultVariable != null) {
                Object resData = resp.getBody();
                task.setVariable(resultVariable.getValue(task).toString(), resData);
            }
            log.info("response of url {}, result:{}", reqUrl, resp);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void notify(DelegateExecution execution) {
        restCall(execution);
    }
}
